package math.vectorSpace.msc;

import math.vectorSpace.RX.Vector;
import math.mathExtended.DoubleComparator;
import math.mathExtended.MathExtended;

/**
 * General class for creating points of any dimension
 * 
 * @author Shimu Wu
 * @version 1.0
 */
public class Point {

    private double[] coord;

    private static final DoubleComparator comparator = new DoubleComparator();

    /**
     * Creates a point at the specified coordinates
     * 
     * @param coord an double[] containing the coordinates for this point 
     */
    public Point(double[] coord) {
        this.coord = copyComponents(coord);
    }

    private double[] copyComponents(double[] components) {
        double[] copy = new double[components.length];
        System.arraycopy(components, 0, copy, 0, components.length);
        return copy;
    }
    
    /**
     * Creates a point at the location of the given point
     * 
     * @param point a Point object
     */
    public Point(Point point) {
        this(point.coord);
    }

    /*
    public Point(Vector vector) {
        this.coord = vector.getCoord();
    }*/

    /**
     * Returns all of this point's coordinates
     * 
     * @param index the index of the point's coordinate, indexing starts at 0
     * @return the coordinate of the point at the given index
     */
    public double getCoord(int index) {
        return coord[index];
    }

    /**
     * Returns the dimension of this point.
     * 
     * @return the dimension of this point
     */
    public int getDimension() {
        return coord.length;
    }

    /**
     * Sets the coordinate of this point at the specified index
     * 
     * @param index the index of the coordinate
     * @param value the value of the coordinate
     */
    public void setCoord(int index, double value) {
        coord[index] = value;
    }

    /**
     * Sets the location of this point to the location of another point
     * @param newPoint a Point object
     */
    public void setPoint(Point newPoint) {
        this.coord = copyComponents(newPoint.coord);
    }

    /**
     * Checks if the given point is the same as this point
     * 
     * @param point a Point object
     * @return true if this point and the given point have the same coordinates, false otherwise
     */
    public boolean equal(Point point) {
        return comparator.equal(coord, point.coord);
    }

    /** 
     * Returns a new Point that is the sum of this point and the given point.
     * This method is useful for moving a point. If moving only in one axis is 
     * required, the other coordinates can simply be set to zero. If subtracting
     * is required simply set the coordinates to negative, the subtract method 
     * can also be used.
     * 
     * @param point a Point object
     * @return a new Point that is the sum of this point and the given point.
     */
    public Point add(Point point) {
        double[] new_coord = new double[this.getDimension()];
        for (int i = 0; i < this.getDimension(); i++) {
            new_coord[i] = coord[i] + point.coord[i];
        }
        return new Point(new_coord);
    }

    /**
     * Returns a new Point that is the difference between this point and the given point
     * 
     * @param point a Point object
     * @return a new Point that is the difference between this point and the given point
     */
    public Point subtract(Point point) {
        double[] new_coord = new double[this.getDimension()];
        for (int i = 0; i < this.getDimension(); i++) {
            new_coord[i] = coord[i] - point.coord[i];
        }
        return new Point(new_coord);
    }

    /**
     * Returns the distance between this point and the given point
     * 
     * @param point a Point object
     * @return the distance between this point and the given point
     */
    public double distance(Point point) {
        return this.subtract(point).distance();
    }

    /**
     * Returns the distance from this point to the origin.
     * 
     * @return the distance from this point to the origin
     */
    public double distance() {
        return Math.sqrt(MathExtended.sumOfSquares(coord));
    }

    /**
     * Returns the String representation of this point of the form:
     * Point [x0, x1, x2, ... xn]
     * 
     * @return the String representation of this point
     */
    public String toString() {
        String return_str = "Point (";
        for (int i = 0; i < this.getDimension(); i++) {
            return_str += Double.toString(coord[i]);
        }
        return_str += ")";

        return return_str;
    }
}
